from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
from fdfs_client.client import Fdfs_client


@deconstructible  # 只有使用装饰器装饰才能成为一个存储类
class FastDFSStorage(Storage):  # 一般工具类为了保持灵活性，都会设置参数为命名参数，
    def __init__(self, base_url=None, client_conf=None):
        if base_url is None:  # base_url为构造图片完整路径的前缀，为服务器域名
            base_url = settings.FDFS_URL
        self.base_url = base_url
        if client_conf is None:  # client_conf 向项目中总配置中添加ＦastDFS客户端配置文件的路径
            client_conf = settings.FDFS_CLIENT_CONF
        self.client_conf = client_conf

    # open和save方法为实现存储类必须实现的方法
    def _open(self, name, mode='rb'):
        """
        由于存储文件，不需要打开文件，所以可以pass
        用不到打开文件，所以省略
        """
        pass

    def _save(self, name, content):
        """
        在FastDFS中保存文件
        :param name: 传入的文件名,不用django去文件是否重复，fastdfs会自动帮我们去筛选。
        :param content: 文件内容
        :return: 保存到数据库中的FastDFS的文件名
        """
        client = Fdfs_client(self.client_conf)  # 开启客户端
        print(type(content))
        ret = client.upload_by_buffer(
            content.read())  # 上传文件的两种方式：１．upload_by_buffer. 2.upload_by_filename.一般不会通过文件名去存储文件，有时候获取不到文件名或者不准确。
        if ret.get("Status") != "Upload successed.":
            raise Exception("upload file failed")
        file_name = ret.get("Remote file_id")
        return file_name  # 存储成功后，返回的id

    def url(self, name):
        """
        返回文件的完整URL路径
        :param name: 数据库中保存的文件名
        :return: 完整的URL
        """
        return self.base_url + name

    def exists(self, name):  # 这个方法会在save方法执行之前运行，只需要设置返回为false就会跳到save方法。
        """
        判断文件是否存在，FastDFS可以自行解决文件的重名问题
        所以此处返回False，告诉Django上传的都是新文件
        :param name:  文件名
        :return: False
        """
        return False
